#include "net.h"
#include "icmp.h"
#include "ip.h"

/**
 * @brief 发送icmp响应(TYPE=0,CODE=0)
 * 
 * @param req_buf 收到的icmp请求包
 * @param src_ip 源ip地址
 */
static void icmp_resp(buf_t *req_buf, uint8_t *src_ip)
{
    // 初始化buf数据部分用来装在req_buf的数据
    // buf_copy(&txbuf,req_buf,req_buf->len);
    // 先申请数据部分的代销
    icmp_hdr_t *req_header = (icmp_hdr_t*)req_buf->data;
    // 取出头后把头去了，便于取出data部分
    buf_remove_header(req_buf,sizeof(icmp_hdr_t));
    // 错误点1：只复制req_buf的data部分
    buf_init(&txbuf,req_buf->len);
    memcpy(txbuf.data,req_buf->data,req_buf->len);
    buf_add_header(&txbuf,sizeof(icmp_hdr_t));
    icmp_hdr_t *header = (icmp_hdr_t*)txbuf.data;

    header->type = ICMP_TYPE_ECHO_REPLY;
    header->code = 0;
    header->id16 = req_header->id16;
    header->seq16 = req_header->seq16;
    // 先填好别的再进行checksum的计算
    header->checksum16 = 0;
    // proved:事实证明，此处的checksum是ckeck所有整个icmp数据包的
    // checksum为全部
    uint16_t csum = checksum16((uint16_t *)txbuf.data,txbuf.len);
    // ckecksum为头部
    // uint16_t csum = checksum16((uint16_t *)txbuf.data,sizeof(icmp_hdr_t));

    header->checksum16 = csum;

    ip_out(&txbuf,src_ip,NET_PROTOCOL_ICMP);
}

/**
 * @brief 处理一个收到的数据包
 * 
 * @param buf 要处理的数据包
 * @param src_ip 源ip地址
 */
void icmp_in(buf_t *buf, uint8_t *src_ip)
{
    if (buf->len<sizeof(icmp_hdr_t))
    {
        return;
    }
    icmp_hdr_t *req_header = (icmp_hdr_t*)buf->data;
    if (req_header->type == ICMP_TYPE_ECHO_REQUEST)
    {
        icmp_resp(buf,src_ip);
    }
    
}

/**
 * @brief 发送icmp不可达
 * 
 * @param recv_buf 收到的ip数据包
 * @param src_ip 源ip地址
 * @param code icmp code，协议不可达或端口不可达
 */
// Q:差错报文的校验范围是只有头部还是所有，当前理解为所有
void icmp_unreachable(buf_t *recv_buf, uint8_t *src_ip, icmp_code_t code)
{
    // 数据部分有28个字节
    buf_init(&txbuf,sizeof(ip_hdr_t)+8);
    // 将recv_buf中前28个字节复制过去（首部20B+数据部分的前8B）
    // 错误点：区分好buf和buf->data
    memcpy(txbuf.data,recv_buf->data,txbuf.len);
    buf_add_header(&txbuf,sizeof(icmp_hdr_t));
    icmp_hdr_t *header = (icmp_hdr_t*)txbuf.data;
    header->type = ICMP_TYPE_UNREACH;
    header->code = code;
    // 这两个字段未用，都为0
    header->seq16 = 0;
    header->id16 = 0;
    // 设置checksum
    header->checksum16 = 0;
    uint16_t csum = checksum16((uint16_t *)txbuf.data,txbuf.len);
    header->checksum16 = csum;

    ip_out(&txbuf,src_ip,NET_PROTOCOL_ICMP);
}

/**
 * @brief 初始化icmp协议
 * 
 */
void icmp_init(){
    net_add_protocol(NET_PROTOCOL_ICMP, icmp_in);
}